﻿using Convertor.Interface;
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using Wpf.CommonTools.SystemExtension;

namespace Convertor.Plugin.WebDownloader
{
    public class Plugin : PluginBase
    {
        #region private

        private object html;
        private Regex regUrl = new Regex("<a\\s.*?href=\"([^\"]+)\"[^>]*>(.*?)</a>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
        private Regex regEmail = new Regex(@"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b", RegexOptions.IgnoreCase | RegexOptions.Multiline);

        #endregion

        #region Static Func

        static string[] LanguagePaths = new string[] { @"Convertor.Plugin.WebDownloader;component\Assets\Lang\StringResource.zh.xaml", @"Convertor.Plugin.WebDownloader;component\Assets\Lang\StringResource.en.xaml" };

        static Plugin()
        {
            foreach (string path in LanguagePaths)
            {
                ResourceDictionary resourceDictionary = new ResourceDictionary();
                resourceDictionary.Source = new Uri(path, UriKind.Relative);
                Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
            }
        }

        #endregion

        #region IPluginInfo 成员

        public override string Name
        {
            get { return UILanguage.FindResource("PluginType_Web") + " - " + UILanguage.FindResource("Plugin_WebDownloader_Name"); }
        }

        public override string Description
        {
            get { return UILanguage.FindResource("Plugin_WebDownloader_Desc"); }
        }

        

        

        #endregion

        #region IConvertParamters 成员

        public override IConvertParamters Output(IConvertParamters content)
        {
            logTo(config.ToolTip, LogTypes.Normal);

            if (content == null || content.OutputContent == null)
            {
                logTo(UILanguage.FindResource("Plugin_Common_Input_Output_Null"), LogTypes.Error | LogTypes.HidePluginName);
                content.StopConvert = true;
                return content;
            }

            html = null;
            bool b;
            Uri uri = getUri(content.OutputContent, out b);

            if (!b)
            {
                return content;
            }
            else
            {
                if (config.CookieType == CookieTypes.LastInputContent)
                {
                    config.Cookie = content.LastInputContent.ToString();
                }
                HttpUtils hu = new HttpUtils(config, uri);
                if (config.HttpMethod == HttpMethods.GET)
                {
                    try
                    {
                        hu.Get();
                    }
                    catch (Exception ex)
                    {
                        logTo(ex.Message, LogTypes.Alert | LogTypes.HidePluginName);
                        return content;
                    }
                }
                else
                {
                    string data;
                    if (config.PostType == PostTypes.LastInputContent)
                    {
                        data = content.LastInputContent.ToString();
                    }
                    else
                    {
                        data = config.PostDate;
                    }
                    try
                    {
                        hu.Post(data);
                        logTo(string.Format(UILanguage.FindResource("Plugin_WebDownloader_PostData"), data), LogTypes.Normal | LogTypes.HidePluginName);
                    }
                    catch (Exception ex)
                    {
                        logTo(ex.Message, LogTypes.Alert | LogTypes.HidePluginName);
                        return content;
                    }
                }

                if (hu.ResponseStream != null)
                {
                    Encoding encoder = config.EncodingContent == null ? null : config.EncodingContent.Encoder;

                    string sContentEncoding = hu.Response.Headers["Content-Encoding"];
                    MemoryStream msTemp = getMsTemp(sContentEncoding, hu.ResponseStream);
                    switch (config.FindType)
                    {
                        case FindTypes.Html:
                            html = getHtml(msTemp, encoder);
                            break;
                        case FindTypes.Url:
                            string str = getHtml(msTemp, encoder);
                            resolveUrl(str, uri);
                            break;
                        case FindTypes.Email:
                            str = getHtml(msTemp, encoder);
                            resolveEmail(str);
                            break;
                        case FindTypes.File:
                            //lastInputPath = Wpf.CommonTools.SystemExtension.MyUrl.GetPageName(uri.ToString());
                            html = msTemp;
                            break;
                        case FindTypes.Header:
                            html = hu.Response.Headers;
                            break;
                    }
                    logTo(string.Format(UILanguage.FindResource("Plugin_WebDownloader_DownloadTarget"), UILanguage.TryFindResource("Plugin_WebDownloader_FindTypes_" + config.FindType)), LogTypes.Normal | LogTypes.HidePluginName);
                    if (hu.Response != null)
                    {
                        hu.Response.Close();
                    }
                    if (hu.ResponseStream != null)
                    {
                        hu.ResponseStream.Close();
                    }
                }
                ConvertParamters output = new ConvertParamters(content.LastInputContent, html ?? "Object.NULL");
                return output;
            }
        }

        private Uri getUri(object output, out bool b)
        {
            string url = output.ToString().Trim();
            Uri uri;

            b = Uri.TryCreate(url, UriKind.Absolute, out uri);
            if (!b)
            {
                try
                {
                    uri = new Uri(url);
                    b = true;
                }
                catch { }
            }
            if (!b)
            {
                logTo(string.Format(UILanguage.FindResource("Plugin_WebDownloader_NotUrl"), url), LogTypes.Alert);
            }
            return uri;
        }

        private MemoryStream getMsTemp(string sContentEncoding, Stream result)
        {
            byte[] buf = new byte[1000];
            MemoryStream msTemp = new MemoryStream();


            if (sContentEncoding == "gzip")
            {
                int count = 0;
                GZipStream gzip = new GZipStream(result, CompressionMode.Decompress);
                while ((count = gzip.Read(buf, 0, buf.Length)) > 0)
                {
                    msTemp.Write(buf, 0, count);
                }
                // end-gzip
                logTo(string.Format(UILanguage.FindResource("Plugin_WebDownloader_DownloadGZip"), config.PostDate), LogTypes.Normal | LogTypes.HidePluginName);
            }
            else
            {
                byte[] readBuffer = new byte[4096];
                long totalDownloadedByte = 0; //下载文件大小   
                int osize = result.Read(buf, 0, (int)buf.Length); //读流   
                while (osize > 0)
                {
                    totalDownloadedByte = osize + totalDownloadedByte; //更新文件大小   
                    msTemp.Write(buf, 0, osize); //写流   
                    osize = result.Read(buf, 0, (int)buf.Length); //读流   
                }
            }

            return msTemp;
        }

        private string getHtml(MemoryStream msTemp, Encoding encode)
        {
            byte[] byteArray = msTemp.ToArray();

            string value = (encode ?? Encoding.UTF8).GetString(byteArray);

            if (encode == null) //自动判断编码
            {
                Match charSetMatch = Regex.Match(value, "(?<= charset=).*?(?=('|\"))", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);
                string webCharSet = charSetMatch.Value.Replace("\"", "").Replace("'", "");
                if (!string.IsNullOrWhiteSpace(webCharSet))
                {
                    try
                    {
                        encode = Encoding.GetEncoding(webCharSet);
                        logTo(string.Format(UILanguage.FindResource("Plugin_WebDownloader_PageCodeChanged"), encode), LogTypes.Normal | LogTypes.HidePluginName);
                    }
                    catch (Exception ex)
                    {
                        logTo(ex.Message, LogTypes.Alert | LogTypes.HidePluginName);
                    }
                    if (encode != null)
                    {
                        value = encode.GetString(byteArray);
                    }
                }
            }
            return value;
        }

        private void resolveUrl(string str, Uri uri)
        {
            MatchCollection mc = regUrl.Matches(str);
            Console.WriteLine(mc.Count);
            StringBuilder ssb = new StringBuilder();
            foreach (Match m in mc)
            {
                string url = m.Groups[1].Value;
                if (url.StartsWith("/"))
                {
                    if (uri.Port == 80)
                    {
                        url = string.Format("{0}://{1}{2}", uri.Scheme, uri.Authority, url);
                    }
                    else
                    {
                        url = string.Format("{0}://{1}:{2}{3}", uri.Scheme, uri.Authority, uri.Port, url);
                    }
                }
                else if (url.StartsWith("../"))
                {
                    Regex reg = new Regex(@"\.\.\/");
                    int number = reg.Matches(url).Count;
                    url = reg.Replace(url, "");
                    string dd = string.Empty;
                    for (int i = 0; i < uri.Segments.Length - number - 1; i++)
                    {
                        dd += (uri.Segments[i]);
                    }
                    if (uri.Port == 80)
                    {
                        url = string.Format("{0}://{1}{2}{3}", uri.Scheme, uri.Authority, dd, url);
                    }
                    else
                    {
                        url = string.Format("{0}://{1}:{2}{3}{4}", uri.Scheme, uri.Authority, uri.Port, dd, url);
                    }
                }
                else if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                ssb.AppendLine(url);
            }
            logTo(string.Format(UILanguage.FindResource("Plugin_WebDownloader_GetUrl"), mc.Count, UILanguage.TryFindResource("Plugin_WebDownloader_FindTypes_" + config.FindType)), LogTypes.Normal | LogTypes.HidePluginName);

            html = ssb;
        }

        private void resolveEmail(string str)
        {
            MatchCollection mc = regEmail.Matches(str);
            StringBuilder ssb = new StringBuilder();
            foreach (Match m in mc)
            {
                ssb.AppendLine(m.Value);
            }
            logTo(string.Format(UILanguage.FindResource("Plugin_WebDownloader_GetUrl"), mc.Count, UILanguage.TryFindResource("Plugin_WebDownloader_FindTypes_" + config.FindType)), LogTypes.Normal | LogTypes.HidePluginName);
            html = ssb;
        }

        #endregion

        #region IPluginConfigForm 成员

        public override PluginConfigForm ConfigForm
        {
            get { return new ConfigForm(); }
        }

        #endregion

        #region IPluginConfig 成员

        private ConfigContent config;
        public override object Config
        {
            get { return config; }
            set { config = (value as ConfigContent) ?? new ConfigContent(); }
        }

        #endregion

    }
}
